#include "JsonParser.h"
#include <vector>


#define CHECK_RETURN(exp)  { if (!(exp)) return false; }

class JImpl
{
public:
	enum EJToken
	{
		EJT_None,
		EJT_String,
		EJT_ObjBeg,
		EJT_ObjEnd,
		EJT_ArrayBeg,
		EJT_ArrayEnd,
		EJT_Comment,
		EJT_Number,
		EJT_True,
		EJT_False,
		EJT_Null,
		EJT_Separator,
		EJT_Define,
	};

	struct JToken
	{
		EJToken Token;
		size_t Pos;
	};

	typedef std::vector<JToken> JTokenList;

	struct JValue
	{
		EJToken Token;
	};

	struct JString : public JValue
	{
		size_t Pos;
		size_t Len;

		bool Match(const wchar_t* str, const wchar_t* data) const
		{
			for (size_t i = 0; i < Len; i++)
			{
				wchar_t c = *str++;
				if (c == 0 || data[Pos + i] != c)
					return false;
			}
			return *str == 0;
		}
	};

	struct JNumber : public JValue
	{
		size_t Pos;
		size_t Len;
	};

	struct JAttrib
	{
		JString Name;
		JValue* Value;
		JAttrib* Next;
	};

	struct JObject : public JValue
	{
		JAttrib* Attribs;
	};

	struct JItem
	{
		JValue* Value;
		JItem* Next;
	};

	struct JArray : public JValue
	{
		JItem* Values;
	};

public:
	JImpl(const std::wstring& dataStr, bool copyData) : mData(dataStr.c_str()), mDataSize(dataStr.size())
	{
		JToken token;
		JTokenList tokens;
		size_t memSize = 0, extraSize = std::max(sizeof(JAttrib), sizeof(JItem));

		if (copyData)
		{
			mCopyData = dataStr;
			mData = mCopyData.c_str();
		}

		for (token.Pos = 0; token.Pos < mDataSize; token.Pos++)
		{
			token.Token = EJT_None;
			switch (mData[token.Pos])
			{
			case L'"':
				memSize += sizeof(JString);
				if (tokens.empty() || tokens.back().Token != EJT_Define)
					memSize += extraSize;

				token.Token = EJT_String;
				tokens.push_back(token);

				// parse to the end of the string.
				for (token.Pos++; token.Pos < mDataSize; token.Pos++)
				{
					if (mData[token.Pos] == '\\') token.Pos++;
					if (mData[token.Pos] == '"') break;
				}
				break;
			case L'{':
				memSize += sizeof(JObject) + extraSize;
				token.Token = EJT_ObjBeg;
				break;
			case L'}':
				token.Token = EJT_ObjEnd;
				break;
			case L'[':
				memSize += sizeof(JArray) + extraSize;
				token.Token = EJT_ArrayBeg;
				break;
			case L']':
				token.Token = EJT_ArrayEnd;
				break;
			case L':':
				token.Token = EJT_Define;
				break;
			case L',':
				token.Token = EJT_Separator;
				break;
			case L'0':
			case L'1':
			case L'2':
			case L'3':
			case L'4':
			case L'5':
			case L'6':
			case L'7':
			case L'8':
			case L'9':
			case L'-':
				memSize += sizeof(JNumber) + extraSize;
				token.Token = EJT_Number;
				tokens.push_back(token);
				token.Pos = dataStr.find_first_not_of(L"0123456789+-.eE", token.Pos) - 1;
				break;
			case L't':
				memSize += sizeof(JValue) + extraSize;
				if (token.Pos + 4 < mDataSize &&
					mData[++token.Pos] == L'r' &&
					mData[++token.Pos] == L'u' &&
					mData[++token.Pos] == L'e')
					token.Token = EJT_True;
				break;
			case L'f':
				memSize += sizeof(JValue) + extraSize;
				if (token.Pos + 5 < mDataSize &&
					mData[++token.Pos] == L'a' &&
					mData[++token.Pos] == L'l' &&
					mData[++token.Pos] == L's' &&
					mData[++token.Pos] == L'e')
					token.Token = EJT_False;
				break;
			case L'n':
				memSize += sizeof(JValue) + extraSize;
				if (token.Pos + 4 < mDataSize &&
					mData[++token.Pos] == L'u' &&
					mData[++token.Pos] == L'l' &&
					mData[++token.Pos] == L'l')
					token.Token = EJT_Null;
				break;
			}

			if (token.Token != EJT_None)
				tokens.push_back(token);
		}

		// alloc buffer and clear for future use.
		mAllocBuff.reserve(memSize);
		mAllocBuff.resize(1);
		memset(&mAllocBuff[0], 0, mAllocBuff.capacity());
		mAllocBuff.clear();

		size_t pos = 0;
		mRoot.Attribs = NULL;
		mRoot.Token = EJT_ObjBeg;

		mGood = ParseObject(&mRoot, tokens, pos);
		if (mGood)
			mParserStack.push_back(&mRoot);
	}

	const wchar_t* Data(size_t pos = 0) const { return &mData[pos]; }
	const JObject* Root() const { return &mRoot; }
	bool Good() const { return mGood; }

private:
	bool mGood;
	std::wstring mCopyData;
	const wchar_t* mData;
	const size_t mDataSize;
	JObject mRoot;
	std::vector<char> mAllocBuff;
	std::vector<const JValue*> mParserStack;

	template<typename T>
	T* Malloc()
	{
		size_t size = sizeof(T);
		if (mAllocBuff.size() + size > mAllocBuff.capacity())
			return NULL;
		size_t pos = mAllocBuff.size();
		mAllocBuff.resize(mAllocBuff.size() + size);
		return (T*)&mAllocBuff[pos];
	}

	bool ParseObject(JObject* obj, const JTokenList& tokens, size_t& pos)
	{
		CHECK_RETURN(tokens[pos].Token == EJT_ObjBeg);
		pos++;

		while (pos < tokens.size())
		{
			JAttrib* attrib = Malloc<JAttrib>();
			attrib->Next = obj->Attribs;
			obj->Attribs = attrib;

			// parse the name.
			if (!ParseString(&attrib->Name, tokens, pos))
				return false;

			// parse the define
			CHECK_RETURN(tokens[pos].Token == EJT_Define);
			pos++;

			// parse the value.
			attrib->Value = ParseValue(tokens, pos);
			if (attrib->Value == NULL)
				return false;

			size_t endFlag = pos++;

			// reach the end of the object.
			if (tokens[endFlag].Token == EJT_ObjEnd)
				return true;

			// it should be a separator: ','
			if (tokens[endFlag].Token != EJT_Separator)
				return false;
		}

		return false;
	}

	bool ParseString(JString* str, const JTokenList& tokens, size_t& pos)
	{
		CHECK_RETURN(pos + 2 < tokens.size());
		CHECK_RETURN(tokens[pos].Token == EJT_String);
		CHECK_RETURN(tokens[pos+1].Token == EJT_String);

		str->Pos = tokens[pos++].Pos + 1;
		str->Len = tokens[pos++].Pos - str->Pos;
		return true;
	}

	bool ParseNumber(JNumber* num, const JTokenList& tokens, size_t& pos)
	{
		CHECK_RETURN(pos + 2 < tokens.size());
		CHECK_RETURN(tokens[pos].Token == EJT_Number);
		CHECK_RETURN(tokens[pos+1].Token == EJT_Number);

		num->Pos = tokens[pos++].Pos;
		num->Len = tokens[pos++].Pos - num->Pos + 1;
		return true;
	}

	bool ParseArray(JArray* arr, const JTokenList& tokens, size_t& pos)
	{
		CHECK_RETURN(pos + 2 < tokens.size());
		CHECK_RETURN(tokens[pos].Token == EJT_ArrayBeg);
		pos++;

		// empty array.
		if (tokens[pos].Token == EJT_ArrayEnd)
			return true;

		JItem* lastItem = NULL;
		while (pos < tokens.size())
		{
			JValue* value = ParseValue(tokens, pos);
			if (value == NULL)
				return false;

			JItem* item = Malloc<JItem>();
			item->Value = value;
			item->Next = NULL;

			// enqueue the item to the last.
			if (lastItem == NULL)
				arr->Values = item;
			else
				lastItem->Next = item;
			lastItem = item;
			
			size_t endFlag = pos++;

			// reach the end of the object.
			if (tokens[endFlag].Token == EJT_ArrayEnd)
				return true;

			// it should be a separator: ','
			if (tokens[endFlag].Token != EJT_Separator)
				return false;
		}

		return false;
	}

	JValue* ParseValue(const JTokenList& tokens, size_t& pos)
	{
		JValue* ret = NULL;
		bool result = false;
		EJToken token = tokens[pos].Token;

		switch (token)
		{
		case EJT_String:
			ret = Malloc<JString>();
			result = ParseString((JString*)ret, tokens, pos);
			break;
		case EJT_Number:
			ret = Malloc<JNumber>();
			result = ParseNumber((JNumber*)ret, tokens, pos);
			break;
		case EJT_ObjBeg:
			ret = Malloc<JObject>();
			result = ParseObject((JObject*)ret, tokens, pos);
			break;
		case EJT_ArrayBeg:
			ret = Malloc<JArray>();
			result = ParseArray((JArray*)ret, tokens, pos);
			break;
		case EJT_True:
		case EJT_False:
		case EJT_Null:
			ret = Malloc<JValue>();
			break;
		}

		// parse failed.
		if (!result && ret)
			return NULL;

		ret->Token = token;
		return ret;
	}

public:
	const JValue* ChildValue(const wchar_t* name) const
	{
		if (mParserStack.empty() || mParserStack.back()->Token != EJT_ObjBeg)
			return false;

		const JObject* current = static_cast<const JObject*>(mParserStack.back());
		const JAttrib* attrib = current->Attribs;
		while (attrib != NULL)
		{
			if (attrib->Name.Match(name, Data()))
				return attrib->Value;
			attrib = attrib->Next;
		}
		return false;
	}

	bool PushChild(const JValue* jValue)
	{
		if (jValue && (jValue->Token == EJT_ObjBeg || jValue->Token == EJT_ArrayBeg))
		{
			mParserStack.push_back(jValue);
			return true;
		}
		return false;
	}

	bool Child(const wchar_t* name)
	{
		return PushChild(ChildValue(name));
	}

	bool Child()
	{
		if (mParserStack.empty())
			return false;

		const JValue* jValue = mParserStack.back();
		if (jValue->Token == EJT_ObjBeg)
		{
			const JObject* jObject = static_cast<const JObject*>(jValue);
			if (jObject->Attribs == NULL)
				return false;

			return PushChild(jObject->Attribs->Value);
		}
		else if (jValue->Token == EJT_ArrayBeg)
		{
			const JArray* jArray = static_cast<const JArray*>(jValue);
			if (jArray->Values == NULL)
				return false;

			return PushChild(jArray->Values->Value);
		}
		return false;
	}

	bool Next()
	{
		if (mParserStack.size() < 2)
			return false;

		const JValue* jParent = mParserStack[mParserStack.size() - 2];
		const JValue* jCurrent = mParserStack.back();
		if (jParent->Token == EJT_ObjBeg)
		{
			const JObject* jObject = static_cast<const JObject*>(jParent);
			const JAttrib* jAttrib = jObject->Attribs;
			while (jAttrib != NULL)
			{
				if (jAttrib->Value == jCurrent && jAttrib->Next)
				{
					mParserStack.resize(mParserStack.size() - 1);
					return PushChild(jAttrib->Next->Value);
				}
				jAttrib = jAttrib->Next;
			}
		}
		else if (jParent->Token == EJT_ArrayBeg)
		{
			const JArray* jArray = static_cast<const JArray*>(jParent);
			const JItem* jItem = jArray->Values;
			while (jItem != NULL)
			{
				if (jItem->Value == jCurrent && jItem->Next)
				{
					mParserStack.resize(mParserStack.size() - 1);
					return PushChild(jItem->Next->Value);
				}
				jItem = jItem->Next;
			}
		}
		return false;
	}

	bool Parent()
	{
		if (mParserStack.size() > 1)
			mParserStack.pop_back();
		return mParserStack.size() > 1;
	}
};

class JsonHelper
{
public:
	// numerice values.
	template<typename T>
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JValue* jValue, T& value)
	{
		double dValue = 0;
		if (!JValueAttrib(jImpl, jValue, dValue))
			return false;
		
		value = static_cast<T>(dValue);
		return true;
	}
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JValue* jValue, double& value)
	{
		if (jValue && jValue->Token == JImpl::EJT_Number)
		{
			const JImpl::JNumber* jNumber = static_cast<const JImpl::JNumber*>(jValue);
			value = _wtof(jImpl->Data(jNumber->Pos));
			return true;
		}
		return false;
	}
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JValue* jValue, __int64& value)
	{
		if (jValue && jValue->Token == JImpl::EJT_Number)
		{
			const JImpl::JNumber* jNumber = static_cast<const JImpl::JNumber*>(jValue);
			value = _wtoi64(jImpl->Data(jNumber->Pos));
			return true;
		}
		return false;
	}
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JValue* jValue, unsigned __int64& value)
	{
		if (jValue && jValue->Token == JImpl::EJT_Number)
		{
			const JImpl::JNumber* jNumber = static_cast<const JImpl::JNumber*>(jValue);
			value = _wcstoui64(jImpl->Data(jNumber->Pos), NULL, 10);
			return true;
		}
		return false;
	}
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JValue* jValue, bool& value)
	{
		if (jValue && (jValue->Token == JImpl::EJT_True || jValue->Token == JImpl::EJT_False))
		{
			value = (jValue->Token == JImpl::EJT_True);
			return true;
		}
		return false;
	}
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JValue* jValue, std::wstring& value)
	{
		if (jValue && (jValue->Token == JImpl::EJT_String))
		{
			const JImpl::JString* jString = static_cast<const JImpl::JString*>(jValue);
			value.assign(jImpl->Data(jString->Pos), jString->Len);
			return true;
		}
		return false;
	}

	template<typename T>
	static bool JValueAttrib(const JImpl* jImpl, const JImpl::JArray* jArray, std::vector<T>& values)
	{
		if (jArray && jArray->Values)
		{
			const JImpl::JItem* jItem = jArray->Values;
			while (jItem != NULL)
			{
				T value;
				if (!JValueAttrib(jImpl, jItem->Value, value))
					return false;
				values.push_back(value);
				jItem = jItem->Next;
			}
			return true;
		}
		return false;
	}
};

JsonParser::JsonParser(const std::wstring& data, bool copyData)
	: mImpl(new JImpl(data, copyData))
{
	// dummy code for template.
	if (mImpl == NULL)
	{
	#define TEMPLATE_GEN(Type) \
		{ Type value; Attrib(L#Type, value); } \
		{ std::vector<Type> values; Array(L#Type, values); }

		TEMPLATE_GEN(char);
		TEMPLATE_GEN(unsigned char);
		TEMPLATE_GEN(short);
		TEMPLATE_GEN(unsigned short);
		TEMPLATE_GEN(int);
		TEMPLATE_GEN(unsigned int);
		TEMPLATE_GEN(float);
		TEMPLATE_GEN(double);
		TEMPLATE_GEN(bool);
		TEMPLATE_GEN(__int64);
		TEMPLATE_GEN(unsigned __int64);
		TEMPLATE_GEN(std::wstring);
	}
}

JsonParser::~JsonParser()
{
	delete mImpl;
}


bool JsonParser::Parent() { return mImpl->Parent(); }
bool JsonParser::Child() { return mImpl->Child(); }
bool JsonParser::Next() { return mImpl->Next(); }
bool JsonParser::Child(const wchar_t* name) { return mImpl->Child(name); }
	
template<typename T>
bool JsonParser::Attrib(const wchar_t* name, T& value)
{
	return JsonHelper::JValueAttrib(mImpl, mImpl->ChildValue(name), value);
}
/////////////////////////////////////////////

template<typename T>
bool JsonParser::Array(const wchar_t* name, std::vector<T>& values)
{
	const JImpl::JValue* jValue = mImpl->ChildValue(name);
	if (jValue == NULL || jValue->Token != JImpl::EJT_ArrayBeg)
		return false;

	const JImpl::JArray* jArray = static_cast<const JImpl::JArray*>(jValue);
	return JsonHelper::JValueAttrib(mImpl, jArray, values);
}
